﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace IGolovkine.AoA.Tests
{

    public class ChannelTestBed<M> : Channel<M>
    {
        private readonly TimeSpan _timeout;
        private readonly ISubject<M> _input;
        private readonly ISubject<M> _output;

        private readonly BlockingStack<Notification<M>> _recentInput = new BlockingStack<Notification<M>>();
        private readonly BlockingStack<Notification<M>> _recentOutput = new BlockingStack<Notification<M>>();


        public ChannelTestBed(TimeSpan timeout) : this(timeout, new Subject<M>(), new Subject<M>())
        {
        }

        public static ChannelTestBed<M> Wrap(TimeSpan timeout, IChannel<M> channel)
        {
            Subject<M> input = new Subject<M>();
            input.Subscribe(channel.Input);

            Subject<M> output = new Subject<M>();
            channel.Output.Subscribe(output);

            return new ChannelTestBed<M>(timeout, input, output);
        }
    
        public ChannelTestBed(TimeSpan timeout, ISubject<M> input, ISubject<M> output) : base(input, output)
        {
            _timeout = timeout;
            _input = input;
            _output = output;

            _input.Subscribe(m => _recentInput.Push(new Notification<M>.OnNext(m)),
            ex => _recentInput.Push(new Notification<M>.OnError(ex)),
            () => _recentInput.Push(new Notification<M>.OnCompleted()));

            _output.Subscribe(m => _recentOutput.Push(new Notification<M>.OnNext(m)),
                ex => _recentOutput.Push(new Notification<M>.OnError(ex)),
               () => _recentOutput.Push(new Notification<M>.OnCompleted()));
        }

        public ISubject<M> InputSubject
        {
            get { return _input; }
        }

        public ISubject<M> OutputSubject
        {
            get { return _output; }
        }

        public Notification<M> PopLastInput()
        {
            try
            {
                return _recentInput.Pop(_timeout);
            }
            catch (TimeoutException ex)
            {
                throw new TimeoutException("PopLastInput()", ex);
            }
        }
        
        public Notification<M> PopLastOutput()
        {
            try
            {
                return _recentOutput.Pop(_timeout);
            }
            catch (TimeoutException ex)
            {
                throw new TimeoutException("PopLastOutput()", ex);
            }
        }
    }

}
